package com.whub507.msgmanager.sso.client.auth;

import com.whub507.msgmanager.sso.context.ContextUser;
import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.List;
import java.util.Map;


/**
 * 提供令牌对象、上下文用户对象创建方法
 * 
 * @author wcl
 *
 */
public class AuthUtils {

	public static ContextUser createContextUser(String token, IdentityToken idToken) {
		ContextUser contextUser = new ContextUser();
		contextUser.setUserID((String) idToken.getUserId());
		contextUser.setUserLoginName((String) idToken.getUserLoginName());
		contextUser.setUserName((String) idToken.getUserName());
		contextUser.setIp(idToken.getClientIp());
		contextUser.setDomainId(idToken.getDomainId());
		contextUser.setEmail(idToken.getEmail());
		contextUser.setToken(token);
		contextUser.setCrossToken(idToken.getCrossToken());
		contextUser.setRoles(idToken.getRoles());
		return contextUser;
	}

	public static IdentityToken createIdentityToken(Map<String, Object> user, String ip, String crossToken,
			List<String> roles, IdentityTokenType type) {
		IdentityToken identityToken = new IdentityToken();
		// 令牌的email=用户表ca_e字段，用于认证和跨级映射
		identityToken.setEmail((String) user.getOrDefault("ca_e", ""));
		identityToken.setUserName((String) user.get("name"));
		identityToken.setUserId((String) user.get("id"));
		identityToken.setUserLoginName((String) user.get("login_name"));
		identityToken.setClientIp(ip);
		identityToken.setDomainId((String) user.get("domain_id"));
		identityToken.setRoles(roles);
		identityToken.setCrossToken(crossToken);
		identityToken.setType(type);
		return identityToken;
	}

	/**
	 * 对字符串输入进行MD5散列
	 * 
	 * @param input
	 * @return MD5散列结果，直接返回字符串，32位小写
	 */
	public static String digestWithMD5(String input) {
		try {
			MessageDigest sha = MessageDigest.getInstance("MD5", "SUN");
			byte[] byteArray = input.getBytes("UTF-8");
			byte[] md5Bytes = sha.digest(byteArray);
			StringBuffer hexValue = new StringBuffer();
			for (int i = 0; i < md5Bytes.length; i++) {
				int val = ((int) md5Bytes[i]) & 0xff;
				if (val < 16) {
					hexValue.append("0");
				}
				hexValue.append(Integer.toHexString(val));
			}
			return hexValue.toString();
		} catch (Exception e) {
			throw new RuntimeException("pt.CryptionManager.digestWithMD5 failed.", e);
		}
	}

	public static void main(String[] args) throws Exception {
		String plainText = "123";

		// 生成一对key：私钥+公钥
		String[] arr = genKeyPair(2048);
		System.out.println("privateKey:" + arr[0]);
		System.out.println("publicKey:" + arr[1]);
		System.out.println("publicKey:" + arr[1].length());

		// 私钥加密
		String pass = encrypt(arr[0], plainText);
		System.out.println("E Text:" + pass);

		// 公钥解密
		plainText = decrypt(arr[1], pass);
		System.out.println("P Text:" + plainText);

		System.out.println(digestWithMD5("1234567890qwertyuiop"));

	}

	/**
	 * 公钥解密
	 * 
	 * @param publicKeyText
	 * @param cipherText
	 * @return
	 * @throws Exception
	 */
	public static String decrypt(String publicKeyText, String cipherText) throws Exception {
		PublicKey publicKey = getPublicKey(publicKeyText);

		return decrypt(publicKey, cipherText);
	}

	private static PublicKey getPublicKey(String publicKeyText) {

		try {
			byte[] publicKeyBytes = Base64.decodeBase64(publicKeyText);
			X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(publicKeyBytes);

			KeyFactory keyFactory = KeyFactory.getInstance("RSA", "SunRsaSign");
			return keyFactory.generatePublic(x509KeySpec);
		} catch (Exception e) {
			throw new IllegalArgumentException("Failed to get public key", e);
		}
	}

	private static String decrypt(PublicKey publicKey, String cipherText) throws Exception {
		Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "SunJCE");
		// try {
		cipher.init(Cipher.DECRYPT_MODE, publicKey);
		// } catch (InvalidKeyException e) {
		// RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
		// RSAPrivateKeySpec spec = new RSAPrivateKeySpec(rsaPublicKey.getModulus(),
		// rsaPublicKey.getPublicExponent());
		// Key fakePrivateKey =
		// KeyFactory.getInstance("RSA","SunRsaSign").generatePrivate(spec);
		// cipher = Cipher.getInstance("RSA","SunJCE"); // It is a stateful object. so
		// we need to
		// get new one.
		// cipher.init(Cipher.DECRYPT_MODE, fakePrivateKey);
		// }

		if (cipherText == null || cipherText.length() == 0) {
			return cipherText;
		}

		byte[] cipherBytes = Base64.decodeBase64(cipherText);
		byte[] plainBytes = cipher.doFinal(cipherBytes);

		return new String(plainBytes);
	}

	/**
	 * 私钥加密
	 * 
	 * @param key
	 * @param plainText
	 * @return
	 * @throws Exception
	 */
	public static String encrypt(String key, String plainText) throws Exception {
		byte[] keyBytes = Base64.decodeBase64(key);
		return encrypt(keyBytes, plainText);
	}

	private static String encrypt(byte[] keyBytes, String plainText) throws Exception {
		PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
		KeyFactory factory = KeyFactory.getInstance("RSA", "SunRsaSign");
		PrivateKey privateKey = factory.generatePrivate(spec);
		Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "SunJCE");
		// try {
		cipher.init(Cipher.ENCRYPT_MODE, privateKey);
		// } catch (InvalidKeyException e) {
		// // For IBM JDK
		// RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey;
		// RSAPublicKeySpec publicKeySpec = new
		// RSAPublicKeySpec(rsaPrivateKey.getModulus(),
		// rsaPrivateKey.getPrivateExponent());
		// Key fakePublicKey =
		// KeyFactory.getInstance("RSA","SunRsaSign").generatePublic(publicKeySpec);
		// cipher = Cipher.getInstance("RSA","SunJCE");
		// cipher.init(Cipher.ENCRYPT_MODE, fakePublicKey);
		// }

		byte[] encryptedBytes = cipher.doFinal(plainText.getBytes("UTF-8"));
		String encryptedString = Base64.encodeBase64String(encryptedBytes);

		return encryptedString;
	}

	private static byte[][] genKeyPairBytes(int keySize) throws NoSuchAlgorithmException, NoSuchProviderException {
		byte[][] keyPairBytes = new byte[2][];

		KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA", "SunRsaSign");
		// 安全扫描：不小于2048
		gen.initialize(Math.max(keySize, 2048), new SecureRandom());
		KeyPair pair = gen.generateKeyPair();

		keyPairBytes[0] = pair.getPrivate().getEncoded();
		keyPairBytes[1] = pair.getPublic().getEncoded();

		return keyPairBytes;
	}

	/**
	 * 生成一对：私钥+公钥
	 * 
	 * @param keySize
	 * @return
	 * @throws NoSuchAlgorithmException
	 * @throws NoSuchProviderException
	 */
	public static String[] genKeyPair(int keySize) throws NoSuchAlgorithmException, NoSuchProviderException {
		byte[][] keyPairBytes = genKeyPairBytes(keySize);
		String[] keyPairs = new String[2];

		keyPairs[0] = Base64.encodeBase64String(keyPairBytes[0]);
		keyPairs[1] = Base64.encodeBase64String(keyPairBytes[1]);

		return keyPairs;
	}

}
